using Acb.AutoMapper;
using Acb.Core.Dependency;
using Acb.Core.Extensions;
using Acb.Core.Helper;
using Acb.Core.Serialize;
using Acb.Core.Timing;
using IdentityModel;
using IdentityServer4.Models;
using IdentityServer4.Stores;
using IdentityServer4.Test;
using Microsoft.EntityFrameworkCore.Internal;
using OAuth.Server.Domain;
using OAuth.Server.Domain.Entities;
using OAuth.Server.Domain.Enums;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace OAuth.Server.OAuth
{
    public static class Extensions
    {
        /// <summary>
        /// Determines whether the client is configured to use PKCE.
        /// </summary>
        /// <param name="store">The store.</param>
        /// <param name="clientId">The client identifier.</param>
        /// <returns></returns>
        public static async Task<bool> IsPkceClientAsync(this IClientStore store, string clientId)
        {
            if (!string.IsNullOrWhiteSpace(clientId))
            {
                var client = await store.FindEnabledClientByIdAsync(clientId);
                return client?.RequirePkce == true;
            }

            return false;
        }

        public static async Task ToDataBase(this IEnumerable<Client> clients)
        {
            var repository = CurrentIocManager.Resolve<ClientRepository>();
            foreach (var client in clients)
            {
                var model = client.MapTo<TClient>();
                model.Id = IdentityHelper.NewSequentialGuid();
                await repository.InsertAsync(model);

                if (client.Claims != null && EnumerableExtensions.Any(client.Claims))
                {
                    var claims = client.Claims.Select(t => new TClientClaim
                    {
                        Id = IdentityHelper.NewSequentialGuid(),
                        Type = t.Type,
                        Value = t.Value,
                        ClientId = model.Id
                    }).ToList();
                    await repository.InsertClaims(claims);
                }
                if (client.Properties != null && EnumerableExtensions.Any(client.Properties))
                {
                    var properties = client.Properties.Select(t => new TClientProperty
                    {
                        Id = IdentityHelper.NewSequentialGuid(),
                        Key = t.Key,
                        Value = t.Value,
                        ClientId = model.Id
                    }).ToList();
                    await repository.InsertProperties(properties);
                }
                if (client.ClientSecrets != null && EnumerableExtensions.Any(client.ClientSecrets))
                {
                    var secrets = client.ClientSecrets.Select(t => new TSecret
                    {
                        Id = IdentityHelper.NewSequentialGuid(),
                        Type = t.Type,
                        Title = t.Description,
                        Data = t.Value,
                        ExpiredTime = t.Expiration,
                        Source = (byte)SecretSource.Client,
                        SourceId = model.Id,
                    }).ToList();
                    await repository.InsertSecrets(secrets);
                }
                var config = new TClientConfig
                {
                    Id = model.Id
                };
                if (!client.AllowedCorsOrigins.IsNullOrEmpty())
                {
                    config.Origins = JsonHelper.ToJson(client.AllowedCorsOrigins);
                }
                if (!client.AllowedGrantTypes.IsNullOrEmpty())
                {
                    config.Grants = JsonHelper.ToJson(client.AllowedGrantTypes);
                }
                if (!client.AllowedScopes.IsNullOrEmpty())
                {
                    config.Scopes = JsonHelper.ToJson(client.AllowedScopes);
                }
                if (!client.IdentityProviderRestrictions.IsNullOrEmpty())
                {
                    config.Providers = JsonHelper.ToJson(client.IdentityProviderRestrictions);
                }
                if (!client.RedirectUris.IsNullOrEmpty())
                {
                    config.Uris = JsonHelper.ToJson(client.RedirectUris);
                }
                if (!client.PostLogoutRedirectUris.IsNullOrEmpty())
                {
                    config.LogoutUris = JsonHelper.ToJson(client.PostLogoutRedirectUris);
                }

                await repository.InsertConfig(config);
            }
        }

        public static async Task ToDataBase(this IEnumerable<ApiResource> apis)
        {
            var repository = CurrentIocManager.Resolve<ResourceRepository>();
            foreach (var api in apis)
            {
                var model = api.MapTo<TResource>();
                model.Id = IdentityHelper.NewSequentialGuid();
                model.Type = (byte)ResourceType.Api;
                model.Title = api.DisplayName;
                await repository.InsertAsync(model);
            }
        }

        public static async Task ToDataBase(this IEnumerable<IdentityResource> identities)
        {
            var repository = CurrentIocManager.Resolve<ResourceRepository>();
            foreach (var identity in identities)
            {
                var model = identity.MapTo<TResource>();
                model.Id = IdentityHelper.NewSequentialGuid();
                model.Type = (byte)ResourceType.Identity;
                model.Title = identity.DisplayName;
                await repository.InsertAsync(model);
            }
        }

        public static async Task ToDataBase(this IEnumerable<TestUser> users)
        {
            var repository = CurrentIocManager.Resolve<AccountRepository>();
            foreach (var user in users)
            {
                var model = new TAccount
                {
                    Id = IdentityHelper.NewSequentialGuid(),
                    Account = user.Username,
                    Mobile = user.Claims.FirstOrDefault(t => t.Type == JwtClaimTypes.PhoneNumber)?.Value,
                    CreateTime = Clock.Now,
                    Email = user.Claims.FirstOrDefault(t => t.Type == JwtClaimTypes.Email)?.Value,
                    PasswordSalt = IdentityHelper.Guid16
                };
                model.Password = $"{user.Password}{model.PasswordSalt}".Md5();
                await repository.InsertAsync(model);
            }
        }
    }
}
